package cn.galudisu.reactivemanifesto.interceptor

import akka.actor.{ActorRef, ActorSystem}
import akka.http.scaladsl.model.{HttpRequest, Uri}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.{CircuitBreakerOpenRejection, Directive0}
import akka.pattern.CircuitBreakerOpenException
import akka.stream.scaladsl.Source
import akka.util.ByteString
import cn.galudisu.reactivemanifesto.GospelMaster.Cabinet
import cn.galudisu.reactivemanifesto._

import scala.concurrent.duration._
import scala.language.{implicitConversions, postfixOps}

case class InterceptorMagnet[T](f: HttpRequest => T)

object InterceptorMagnet {

  implicit def forMessageFromInterrupt(interrupt: (Booklet,Interrupt))(implicit system:ActorSystem): InterceptorMagnet[Directive0] =
    forMessageFromFullInterrupt(MessageEntry(interrupt))

  implicit def forMessageFromFullInterrupt(interrupt: MessageEntry)(implicit system:ActorSystem): InterceptorMagnet[Directive0] = {
    import cn.galudisu.reactivemanifesto.GospelConfig._
    if(enabled) {
      interrupt.mark._2.breaker ! Cabinet(interrupt.mark._1.apis)
      InterceptorMagnet(rupt ⇒ interrupt.handle(rupt))
    }else {
      InterceptorMagnet(_ => pass)
    }
  }

  trait Entry {
    def handle(request: HttpRequest)(implicit system:ActorSystem): Directive0
  }

  /** 拦截入口 */
  case class MessageEntry(mark: (Booklet,Interrupt)) extends Entry {

    override def handle(request: HttpRequest)(implicit system:ActorSystem): Directive0 = {
      val board: CircuitBoard = analyze(mark._1, request.uri)(mark._2.choreography)
      board.junction match {
        case Left(z: Zeroline) =>
          reject(CircuitBreakerOpenRejection(
            new CircuitBreakerOpenException(z.timeout)))
        case Right(_) =>
          pass
      }
    }
  }

  private def analyze(mark: Booklet, uri: Uri)(implicit ref:ActorRef): CircuitBoard = {
    val ch = Switchgear(mark).emit(uri)
    CircuitBoard(uri, ch)
  }

  case class CircuitBoard(entercl: Uri, junction: Either[Zeroline, AC])

  trait Circuity {
    def name: String
    def flux: Option[Source[ByteString, Any]]
  }

  case class AC(name: String,
                flux: Option[Source[ByteString, Any]] = None) extends Circuity

  case class Zeroline(name: String,
                      flux: Option[Source[ByteString, Any]] = None,
                      timeout: FiniteDuration) extends Circuity
}
